#include "sg4c.h"
#include <time.h>
#include <math.h>

typedef struct{
    float x, y;
    float px, py;
    float velx, vely;
    float accx, accy;
    float colorhint;
} particle_t;

typedef struct{
    float x, y;
    float force;
} attractor_t;


enum GLOBAL_CONST{
    PARTICLE_COUNT = 1000,
    ATTRACTOR_COUNT = 20,
    FPS = 30,
    TIME_DIVISION=2,

    MAX_INIT_VEL=30,
    MAX_INIT_ACC=20,
};


particle_t particles[PARTICLE_COUNT];
attractor_t attractors[ATTRACTOR_COUNT];

inline float distance(float ax, float ay, float bx, float by){
    return sqrtf( powf(ax-bx, 2) + powf(ay-by, 2) );
}

void setup(){
    srand(time(NULL));
    int i;
    for( i=0; i<PARTICLE_COUNT; ++i ){
        particle_t * p = &particles[i];
        p->px = p->x = sgWidth()/2;
        p->py = p->y = sgHeight()/2;
        p->velx = rand()%MAX_INIT_VEL-MAX_INIT_VEL/2;
        p->vely = rand()%MAX_INIT_VEL-MAX_INIT_VEL/2;
        p->accx = rand()%MAX_INIT_ACC-MAX_INIT_ACC/2;
        p->accy = rand()%MAX_INIT_ACC-MAX_INIT_ACC/2;
        p->colorhint=i;
    }


    for( i=0; i<ATTRACTOR_COUNT; ++i ){
        attractor_t * atr = &attractors[i];
        atr->x = rand() % sgWidth();
        atr->y = rand() % sgHeight();
        atr->force = -400-(rand()%400);           //-ve for attraction
    }
}


void draw(){
    static int runOnce=1;
    static int count=0;
    int i;
    if( runOnce ){
        sgNoStroke();
        sgFill( sgGray(0) );
        sgRect( 0, 0, sgWidth(), sgHeight() );

        //draw attractors
        sgNoFill();
        sgStroke( sgGray(0x33) );
        sgStrokeWeight(1);
        for( i=0; i<ATTRACTOR_COUNT; ++i ){
            sgEllipse( attractors[i].x-5, attractors[i].y-5, 10, 10 );
        }

        runOnce=0;
    }
    sgNoStroke();
    sgFill( sgHexRGBA(0x3) );
    sgRect( 0, 0, sgWidth(), sgHeight() );
    if(count++>FPS*10){
        //randomly place attractors


        count=0;
        sgNoFill();
        sgStroke( sgGray(0x33) );
        sgStrokeWeight(1);
        for( i=0; i<ATTRACTOR_COUNT; ++i ){
            attractors[i].x = rand()%sgWidth();
            attractors[i].y = rand()%sgHeight();
            sgEllipse( attractors[i].x-5, attractors[i].y-5, 10, 10 );
        }

    }



    //draw
    sgStrokeWeight(2);
    for( i=0; i<PARTICLE_COUNT; ++i ){
        particle_t * const p = &particles[i];
        sgStroke(sgRGBA(200, 255*p->colorhint/PARTICLE_COUNT, 128, 60));
        sgLine( p->px, p->py, p->x, p->y );

        //save previous position;
        p->px = p->x;
        p->py = p->y;
    }

    //update physics
    const float deltaT = 1.0f/FPS/TIME_DIVISION;

    int t;
    for( t=0; t<TIME_DIVISION; ++t ){



        for( i=0; i<PARTICLE_COUNT; ++i ){
            particle_t * p = &particles[i];

            //check out-of-bound
            if( abs( p->x ) > sgWidth() || abs(p->y) > sgHeight() ){
                p->px = p->x = sgWidth()/2;
                p->py = p->y = sgHeight()/2;
                p->velx = rand()%MAX_INIT_VEL-MAX_INIT_VEL/2;
                p->vely = rand()%MAX_INIT_VEL-MAX_INIT_VEL/2;
                p->accx = rand()%MAX_INIT_ACC-MAX_INIT_ACC/2;
                p->accy = rand()%MAX_INIT_ACC-MAX_INIT_ACC/2;
            }


            //update position base on current velocity
            p->x += deltaT * p->velx;
            p->y += deltaT * p->vely;

            //update velocity base on current acceleration
            p->velx += deltaT * p->accx;
            p->vely += deltaT * p->accy;

            //update velocity base on current position
            int j;
            for( j=0; j<ATTRACTOR_COUNT; ++j ){
                attractor_t * const attr = &attractors[j];
                const float dist = distance( p->x, p->y, attr->x, attr->y);
                const float cosine = (p->x - attr->x) / dist;
                const float sine = (p->y - attr->y) / dist;

                float force = attr->force*(1.0f/powf(dist, 2))/TIME_DIVISION;

                p->accx += cosine * force;
                p->accy += sine * force;
            }

        }
    }
}


//events
void mouseMoved(){
    //Mouse moved
}

void mousePressed(){
    //A mouse button is pressed
}

void mouseReleased(){
    //A mouse button is released
}

void mouseClicked(){
    //A mouse button is pressed and released
}

void mouseDragged(){
    //The mouse moves when being pressed
}

void mouseEntered(){
    //The mouse pointer enters the display frame
}

void mouseExited(){
    //The mouse pointer leaves the display frame
}

void keyReleased(){
    //A key is released
}

void keyPressed(){
    //A key is pressed
}

void keyTyped(){
    //A key is pressed and then released
}

int main(){
    sg_config_t config;
    sgInitConfig( &config );

    config.screen.width = 800;  //set the horizontal resolution of the frame
    config.screen.height = 600; //set the vertical resolution of the frame
    config.framerate = FPS;      //set the per-second framerate


    config.smooth = 1; // uncomment this line to enable smoothing

    //------------------------------------------------------------------
    //DO NOT MODIFY CODE BELOW

    //setup callbacks
    config.setup = setup;
    config.draw = draw;

    config.mousePressed = mousePressed;
    config.mouseReleased = mouseReleased;
    config.mouseMoved = mouseMoved;
    config.mouseEntered = mouseEntered;
    config.mouseExited = mouseExited;
    config.mouseClicked = mouseClicked;
    config.mouseDragged = mouseDragged;

    config.keyReleased = keyReleased;
    config.keyPressed = keyPressed;
    config.keyTyped = keyTyped;

    //initilize the system
    sgSetup(&config);

    //run forever
    return sgRunForever();
}
